/****************************************************************************
 * Elisa 1.0                                                                *
 *--------------------------------------------------------------------------*
 * Author: Laurent Granvilliers, Vincent Sorin                              *
 * Copyright (c) 2004 Laboratoire d'Informatique de Nantes Atlantique       *
 *--------------------------------------------------------------------------*
 * Elisa is distributed WITHOUT ANY WARRANTY. Read the associated           *
 * COPYRIGHT file for more details.                                         *
 *--------------------------------------------------------------------------*
 * elisa_interval_gaol.h                                                    *
 ****************************************************************************/

#ifndef _ELISA_ICODE_GAOL_H
#define _ELISA_ICODE_GAOL_H

#define GAOL_EXTENDED     // use of extended mode of gaol
#define GAOL_NAMESPACES   // gaol symbols defined in namespace gaol

#include <iostream>
#include <string>
#include <math.h>         // use of nextafter
#include <gaol/gaol>      // use of gaol
#undef VERSION // undefine VERSION to avoid conflict with Lcc

NAMESPACE_ELISA

/**
 * @ingroup Interval
 * @var typedef double Bound
 * @brief To be defined for the Interval class
 */
typedef double Bound;

// To define if the bounds of intervals
// are floating-point numbers in double precision
#define HAVE_DOUBLE_BOUND

/**
 * @brief To be defined for the Interval class
 */
typedef gaol::interval Icode;


/**
 * @ingroup Interval
 * @class GaolIcodeTraits
 * @brief Implementation of the icode traits for gaol
 */
class GaolIcodeTraits
{
public:
	//@{
	// Constants
	static inline const Icode RealSet()
	{
		return Icode::universe;
	}
	static inline const Icode Zero()
	{
		return Icode::zero;
	}
	static inline const Icode One()
	{
		return Icode::one;
	}
	static inline const Icode Unit()
	{
		return Icode(0.0, 1.0);
	}

	static inline const Icode EmptySet()
	{
		return Icode::emptyset;
	}

	static inline const Icode Pi()
	{
		return Icode::pi;
	}

	static inline const double MaxBound()
	{
		return Icode::universe.right();
	}

	static inline const double MinBound()
	{
		return Icode::universe.left();
	}
	//@}

	//@{
	// Constructors
	static inline void cons(Icode & I,
		const double & x,
		const double & y)
	{
		I = Icode(x, y);
	}

	static inline void cons(Icode & I,
		const std::string & x,
		const std::string & y)
	{
		const char *c = ("[" + x + "," + y + "]").c_str();
		I = Icode(c);
	}

#ifndef HAVE_DOUBLE_BOUND
	static inline void cons(Icode & I,
		const Bound & x,
		const Bound & y)
	{
		I = Icode(x, y);
	}
#endif

	// Destructor
	static inline void del(Icode & x)
	{
	}

	// Modifications
	static inline void set(Icode & x,
		const Icode & y)
	{
		x = y;
	}

	static inline void setEmpty(Icode & x)
	{
		x = Icode::emptyset;
	}

	// Bound operations
	static inline Bound inf(const Icode & x)
	{
		return x.left();
	}

	static inline Bound sup(const Icode & x)
	{
		return x.right();
	}

	static inline Bound next(const Bound & r)
	{
		return gaol::next_float(r);
	}

	static inline Bound prev(const Bound & r)
	{
		return gaol::previous_float(r);
	}

	static inline double convert(const Bound & r)
	{
		return r;
	}

	static inline Bound mid(const Icode & x)
	{
		return x.midpoint();
	}

	static inline Bound midInteger(const Icode & x)
	{
		return ceil(x.midpoint());
	}

	static inline Bound mig(const Icode & x)
	{
		return x.mig();
	}

	static inline Bound mag(const Icode & x)
	{
		return x.mag();
	}

	static inline Bound width(const Icode & x)
	{
		return x.width();
	}


	static inline Icode toInteger(const Icode & x)
	{
		return integer(x);
	}


	static inline bool isEmpty(const Icode & x)
	{
		return x.is_empty();
	}

	static inline bool isPoint(const Icode & x)
	{
		return x.is_a_double();
	}

	static inline bool isZero(const Icode & x)
	{
		return x.is_zero();
	}

	static inline bool isCanonical(const Icode & x)
	{
		return x.is_canonical();
	}

	static inline bool isAnInt(const Icode & x)
	{
		return x.is_an_int();
	}

	static inline bool isPrecise(const Icode & x,
		unsigned int n)
	{
		return(gaol::nb_fp_numbers(x.left(), x.right()) <= n);
	}

	static inline bool overlaps(const Icode & x,
		const Icode & y)
	{
		return !x.set_disjoint(y);
	}



	// Comparisons
	static inline bool isSetEqual(const Icode & x,
		const Icode & y)
	{
		return x.set_eq(y);
	}

	static inline bool isPossiblyEqual(const Icode & x,
		const Icode & y)
	{
		return x.possibly_eq(y);
	}

	static inline bool isCertainlyEqual(const Icode & x,
		const Icode & y)
	{
		return x.certainly_eq(y);
	}

	static inline bool isSetLessThan(const Icode & x,
		const Icode & y)
	{
		return x.set_le(y);
	}

	static inline bool isPossiblyLessThan(const Icode & x,
		const Icode & y)
	{
		return x.possibly_leq(y);
	}

	static inline bool isCertainlyLessThan(const Icode & x,
		const Icode & y)
	{
		return x.certainly_le(y);
	}

	// Arithmetic operations
	static inline Icode & addeq(Icode & x,
		const Icode & y)
	{
		return x += y;
	}

	static inline Icode & subeq(Icode & x,
		const Icode & y)
	{
		return x -= y;
	}

	static inline Icode & muleq(Icode & x,
		const Icode & y)
	{
		return x *= y;
	}

	static inline Icode & diveq(Icode & x,
		const Icode & y)
	{
		return x /= y;
	}

	static inline Icode min(const Icode & x,
		const Icode & y)
	{
		return gaol::min(x, y);
	}

	static inline Icode max(const Icode & x,
		const Icode & y)
	{
		return gaol::max(x, y);
	}

	static inline Icode add(const Icode & x,
		const Icode & y)
	{
		return x + y;
	}

	static inline Icode mul(const Icode & x,
		const Icode & y)
	{
		return x * y;
	}

	static inline Icode sub(const Icode & x,
		const Icode & y)
	{
		return x - y;
	}

	static inline Icode unarysub(const Icode & x)
	{
		return -x;
	}

	static inline Icode div(const Icode & x,
		const Icode & y)
	{
		return x / y;
	}

	static inline Icode sqr(const Icode & x)
	{
		return gaol::sqr(x);
	}

	static inline Icode sqrt(const Icode & x)
	{
		return gaol::sqrt(x);
	}

	static inline Icode abs(const Icode & x)
	{
		return gaol::abs(x);
	}


	static inline Icode pow(const Icode & x, int n)
	{
		return gaol::pow(x, n);
	}

	static inline Icode nth_root(const Icode & x, int n)
	{
		return gaol::nth_root(x, n);
	}

	static inline Icode log(const Icode & x)
	{
		return gaol::log(x);
	}

	static inline Icode exp(const Icode & x)
	{
		return gaol::exp(x);
	}

	static inline Icode cos(const Icode & x)
	{
		return gaol::cos(x);
	}

	static inline Icode acos(const Icode & x)
	{
		return gaol::acos(x);
	}

	static inline Icode sin(const Icode & x)
	{
		return gaol::sin(x);
	}

	static inline Icode asin(const Icode & x)
	{
		return gaol::asin(x);
	}

	static inline Icode tan(const Icode & x)
	{
		return gaol::tan(x);
	}

	static inline Icode atan(const Icode & x)
	{
		return gaol::atan(x);
	}

	static inline Icode cosh(const Icode & x)
	{
		return gaol::cosh(x);
	}

	static inline Icode acosh(const Icode & x)
	{
		return gaol::acosh(x);
	}

	static inline Icode sinh(const Icode & x)
	{
		return gaol::sinh(x);
	}

	static inline Icode asinh(const Icode & x)
	{
		return gaol::asinh(x);
	}

	static inline Icode tanh(const Icode & x)
	{
		return gaol::tanh(x);
	}

	static inline Icode atanh(const Icode & x)
	{
		return gaol::atanh(x);
	}

	static inline Icode asin_rel(const Icode & J, const Icode & I)
	{
		return gaol::asin_rel(J, I);
	}

	static inline Icode acos_rel(const Icode & J, const Icode & I)
	{
		return gaol::acos_rel(J, I);
	}

	static inline Icode atan_rel(const Icode & J, const Icode & I)
	{
		return gaol::atan_rel(J, I);
	}

	static inline Icode asinh_rel(const Icode & J, const Icode & I)
	{
		return gaol::asin_rel(J, I);
	}

	static inline Icode acosh_rel(const Icode & J, const Icode & I)
	{
		return gaol::acos_rel(J, I);
	}

	static inline Icode atanh_rel(const Icode & J, const Icode & I)
	{
		return gaol::atan_rel(J, I);
	}

	static inline Icode sqrt_rel(const Icode & J, const Icode & I)
	{
		return gaol::sqrt_rel(J, I);
	}

	static inline Icode nth_root_rel(const Icode & J, int n, const Icode & I)
	{
		return gaol::nth_root_rel(J, n, I);
	}

	static inline Icode invabs_rel(const Icode & J, const Icode & I)
	{
		return gaol::invabs_rel(J, I);
	}

	static inline Icode div_rel(const Icode & K, const Icode & J, const Icode & I)
	{
		return gaol::div_rel(K, J, I);
	}



	// Bisection
	static inline void split(const Icode & x,
		Icode & y1,
		Icode & y2)
	{
		x.split(y1, y2);
	}

	// Bisection
	static inline void splitToInteger(const Icode & x,
		Icode & y1,
		Icode & y2)
	{
		//the bounds of x are necessarily integers

		Bound m = x.midpoint();

		// Is m an integer?
		if (double (long (m)) == m) {
			y1 = Icode(x.left(), m);
			y2 = Icode(m + 1, x.right());
		} else {
			y1 = Icode(x.left(), floor(m));
			y2 = Icode(ceil(m), x.right());
		}
	}

	// Intersection
	static inline Icode intersect(const Icode & x,
		const Icode & y)
	{
		return x & y;
	}

	// Union
	static inline Icode unio(const Icode & x,
		const Icode & y)
	{
		return x | y;
	}

	// Convex hull
	static inline Icode hull(const Icode & x,
		const Icode & y)
	{
		if (x.is_empty()) {
			return y;
		} else {
			if (y.is_empty()) {
				return x;
			} else {
				//return Icode(gaol::minimum(x.left(), y.left()), gaol::maximum(x.right(), y.right()));
				return Icode(std::min(x.left(), y.left()), std::max(x.right(), y.right()));
			}
		}
	}

	// Output on streams
	static inline void print(std::ostream & os, const Icode & x)
	{
		if (x.left() == x.right()) os << x.left();
		else os << x;
	}

	static void printAsRect(std::ostream & os, const Icode & x, const Icode & y)
	{
		os << x.left()  << ' ' << y.left() << std::endl;
		os << x.left()  << ' ' << y.right() << std::endl;
		os << x.right() << ' ' << y.right() << std::endl;
		os << x.right() << ' ' << y.left() << std::endl;
		os << x.left()  << ' ' << y.left() << std::endl;
		os << std::endl;
	}

	static inline void setOutputDigits(int n)
	{
		Icode::precision(n);
	}

	//@}
};

typedef GaolIcodeTraits IcodeTraits;


END_NAMESPACE_ELISA

#endif
